package com.longtop.framework.base;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.ibatis.SqlMapClientTemplate;
import org.wltea.expression.ExpressionEvaluator;
import org.wltea.expression.datameta.Variable;

import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.engine.execution.SqlExecutor;
import com.ibatis.sqlmap.engine.impl.SqlMapClientImpl;
import com.ibatis.sqlmap.engine.mapping.statement.MappedStatement;
import com.ibatis.sqlmap.engine.scope.SessionScope;
import com.ibatis.sqlmap.engine.scope.StatementScope;
import com.longtop.framework.common.QueryConditions;
import com.longtop.framework.common.QueryResult;
import com.longtop.framework.common.ResourceConstants;
import com.longtop.framework.orm.IBatisSqlExecutor;
import com.longtop.framework.orm.IBatisUtils;
import com.longtop.framework.security.BaseCheckedException;
import com.longtop.framework.security.BaseUncheckedException;
import com.longtop.framework.security.SessionObject;
import com.longtop.framework.sys.flow.model.TSysFlowInstance;
import com.longtop.framework.sys.flow.model.TSysFlowNode;
import com.longtop.framework.sys.flow.model.TSysFlowParam;
import com.longtop.framework.sys.flow.model.TSysFlowRoute;
import com.longtop.framework.sys.flow.model.TSysFlowStep;
import com.longtop.framework.sys.flow.model.TSysFlowTeam;
import com.longtop.framework.sys.user.model.TSysUser;
import com.longtop.framework.util.BeanUtil;
import com.longtop.framework.util.DateUtil;
import com.longtop.framework.util.ReflectUtil;
import com.longtop.framework.util.StringUtil;
import com.longtop.framework.util.UniqueKeyUtil;
import com.longtop.intelliweb.action.IActionContext;
import com.longtop.intelliweb.view.Dataset;

/**
 * 整合Hibernate和iBatis两种orm框架；使用时请详细看注释
 * 
 * Hibernate单表操作，单表查询。 iBatis做多表查询
 * 
 * @author 胡砥峰
 * 
 */
public class BaseService {

	public static final String BEAN_ID = "baseService";

	private boolean isPrintIbatisSql = false; // 是否打印iBatis的sql；测试时设为 true

	private SessionFactory sessionFactory; // Hibernate工厂
	private SqlMapClient sqlMapClient; // iBatis工厂
	private SqlMapClientTemplate sqlMapClientTemplate;// iBatis模板
	private HibernateTemplate hibernateTemplate; // Hibernate模板
	private SqlExecutor sqlExecutor; // 改写iBatis的sql执行器，；解决iBatis跨数据库分页问题

	/**
	 * 初始化的时候，将改写的跨数据库orm执行器，反射注入到框架；解决iBatis跨数据库分页问题
	 * 
	 * @throws Exception
	 */
	public void init() throws Exception {
		if (this.sqlExecutor != null) {
			SqlMapClient sqlMapClient = this.getSqlMapClientTemplate().getSqlMapClient();
			if (sqlMapClient instanceof SqlMapClientImpl)
				ReflectUtil.setFieldValue(((SqlMapClientImpl) sqlMapClient).getDelegate(), "sqlExecutor", SqlExecutor.class, this.sqlExecutor);
		}
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	public void setSqlMapClient(SqlMapClient sqlMapClient) {
		this.sqlMapClient = sqlMapClient;
	}

	public SqlMapClientTemplate getSqlMapClientTemplate() {
		// 首先检查原来的SqlMapClientTemplate实例是否存在
		if (this.sqlMapClientTemplate == null) {
			// 如果不存在，则创建一个SqlMapClientTemplate实例
			this.sqlMapClientTemplate = new SqlMapClientTemplate(this.sqlMapClient);
		}

		return this.sqlMapClientTemplate;
	}

	public HibernateTemplate getHibernateTemplate() {
		// 首先检查原来的HibernateTemplate实例是否存在
		if (this.hibernateTemplate == null) {
			// 如果不存在，则创建一个HibernateTemplate实例
			this.hibernateTemplate = new HibernateTemplate(this.sessionFactory);
		}
		return this.hibernateTemplate;
	}

	public SqlExecutor getSqlExecutor() {
		return this.sqlExecutor;
	}

	public void setSqlExecutor(SqlExecutor sqlExecutor) {
		this.sqlExecutor = sqlExecutor;
	}

	/**
	 * Hibernate 根据主键取对象
	 * 
	 * @param <T>
	 * @param entityClass
	 * @param id
	 * @return
	 */
	public <T> T get(Class entityClass, Serializable id) {
		return (T) this.getHibernateTemplate().get(entityClass, id);
	}

	/**
	 * Hibernate 保存对象
	 * 
	 * @param entity
	 * @throws DataAccessException
	 */
	public void save(Object entity) throws DataAccessException {
		this.getHibernateTemplate().save(entity);
	}

	/**
	 * Hibernate 保存或更新对象
	 * 
	 * @param entity
	 * @throws DataAccessException
	 */
	public void saveOrUpdate(Object entity) throws DataAccessException {
		this.getHibernateTemplate().saveOrUpdate(entity);
	}

	/**
	 * Hibernate 更新对象
	 * 
	 * @param entity
	 * @throws DataAccessException
	 */
	public void update(Object entity) throws DataAccessException {
		this.getHibernateTemplate().update(entity);
	}

	/**
	 * Hibernate 删除对象
	 * 
	 * @param entity
	 * @throws DataAccessException
	 */
	public void delete(Object entity) throws DataAccessException {
		this.getHibernateTemplate().delete(entity);
	}

	/**
	 * Hibernate 根据主键删除对象
	 * 
	 * @param entityClass
	 * @param id
	 * @throws DataAccessException
	 */
	public void delete(Class entityClass, Serializable id) throws DataAccessException {
		this.getHibernateTemplate().delete(this.get(entityClass, id));
	}

	/**
	 * Hibernate 删除集合中所有的对象
	 * 
	 * @param entities
	 * @throws DataAccessException
	 */
	public void deleteAll(Collection entities) throws DataAccessException {
		long time1 = System.currentTimeMillis(); // 开始执行时间

		this.getHibernateTemplate().deleteAll(entities);

		long time2 = System.currentTimeMillis(); // 结束执行时间
		if (isPrintIbatisSql)
			System.out.println("【Hibernate 执行update时间:" + (time2 - time1) + "ms】");
	}

	/**
	 * 根据条件使用HQL语句查询数据
	 * 
	 * 【1】支持查询分页，该方法会利用数据库本身的分页技术实现。
	 * 
	 * 【2】如果数据库(如MySQL,Oracle,SQLServer2005等)支持limit n,m，查询效率最高；
	 * 
	 * 【3】如果数据库(如informix,Sybase12.5.3,SQLServer等)支持top n，查询效率次之（查询结果越大，效率越低）；
	 * 
	 * 【4】如果以上两种均不支持，查询效率最低。
	 * 
	 * 【5】支持查询总记录数;支持order by,group by,having等
	 * 
	 * 【6】不支持功能：不支持select里的嵌套子查询。如不允许这种用法：select a,b,(select c from table1) as d from table2 ...
	 * 
	 * 【7】不支持功能：条件与条件之间不支持or，而是用and。
	 * 
	 * @param hql
	 *            HQL查询语句（不带Where条件）
	 * @param propertyNames
	 *            查询条件的属性名列表
	 * @param operators
	 *            查询条件的操作符列表，应与属性名列表一一对应。操作符包括=, >=, <=, >, <, !=, like
	 * @param values
	 *            查询条件的值列表，该列表应当与属性列表一一对应
	 * @param offset
	 *            查询结果的起始行，从0开始。如果不需要，则设置为-1。
	 * @param size
	 *            查询结果的最大行数。如果不需要，则设置为-1
	 * @param isTotalSize
	 *            是否需要返回本次查询的总记录数，默认false
	 * @param orderBy
	 *            排序字符串,不含order by字符串，如orderBy=a desc,b asc,最后生成为：order by a desc,b asc
	 * @param groupBy
	 *            分组字符串,不含group by 字符串，如groupBy=a, b;,最后生成为：group by a ,b
	 * @param otherCause
	 *            where后面的其它语句，如排序(order by),分组(group by)及聚集(having)。 如group by name order by name desc;
	 * 
	 * @return 有两个值，第一个值表示查询结果列表List list = (List)Object[0] 第二个表示查询返回的总记录数，int count = ((Integer)Object[1]).intValue;
	 * 
	 * @throws BaseUncheckedException
	 */
	public Object[] query(final String hql, final String[] propertyNames, final String[] operators, final Object[] values, final int offset, final int size, final boolean isTotalSize, final String orderBy, final String groupBy, final String otherCause) throws BaseUncheckedException {

		long time1 = System.currentTimeMillis(); // 开始执行时间

		Map map = (Map) this.getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, BaseUncheckedException, SQLException {
				Query query;
				String countSql;
				String fullSql;
				Integer count = new Integer(0);
				Map map = new HashMap();
				String where = "";

				if (hql == null || hql.trim().equals("")) {
					throw new BaseUncheckedException(ResourceConstants.E_BASE_0000);
				}

				if (propertyNames != null && propertyNames.length > 0 && values != null && values.length > 0) {
					if (propertyNames.length != values.length) {
						throw new BaseUncheckedException(ResourceConstants.E_BASE_0001, new Object[] { new Integer(propertyNames.length), new Integer(values.length) });
					}

					if (operators != null && propertyNames.length != operators.length) {
						throw new BaseUncheckedException(ResourceConstants.E_BASE_0002, new Object[] { new Integer(propertyNames.length), new Integer(operators.length) });
					}

					for (int i = 0; i <= propertyNames.length - 1; i++) {
						if ("".equals(where)) {
							where = " where ";
						} else {
							where += "and ";
						}
						if (operators != null && operators[i].equalsIgnoreCase("isnull")) {
							where += propertyNames[i] + " is null ";
						} else if (operators != null && operators[i].equalsIgnoreCase("isnotnull")) {
							where += propertyNames[i] + " is not null ";
						} else if (operators != null && operators[i].equalsIgnoreCase("isempty")) {
							where += propertyNames[i] + " = '' ";
						} else if (operators != null && operators[i].equalsIgnoreCase("isnotempty")) {
							where += propertyNames[i] + " <> '' ";
						} else {
							where += propertyNames[i] + (operators == null || operators[i] == null ? "=" : " " + operators[i]) + " ? ";
						}
					}

					fullSql = hql + where;
					fullSql += orderBy == null || orderBy.trim().equals("") ? "" : " order by " + orderBy;
					fullSql += groupBy == null || groupBy.trim().equals("") ? "" : " group by " + groupBy;
					fullSql += otherCause == null || otherCause.trim().equals("") ? "" : " " + otherCause;

					query = session.createQuery(fullSql);

					for (int i = 0; i <= values.length - 1; i++) {
						if (operators != null && operators[i].equalsIgnoreCase("isnull"))
							continue;
						if (operators != null && operators[i].equalsIgnoreCase("isnotnull"))
							continue;
						if (operators != null && operators[i].equalsIgnoreCase("isempty"))
							continue;
						if (operators != null && operators[i].equalsIgnoreCase("isnotempty"))
							continue;
						query.setParameter(i, values[i]);
					}
				} else {

					fullSql = hql + where;
					fullSql += orderBy == null || orderBy.trim().equals("") ? "" : " order by " + orderBy;
					fullSql += groupBy == null || groupBy.trim().equals("") ? "" : " group by " + groupBy;
					fullSql += otherCause == null || otherCause.trim().equals("") ? "" : " " + otherCause;

					query = session.createQuery(fullSql);
				}

				// 如果需要统计本次查询总记录数
				if (isTotalSize) {

					// 生成统计总数查询语句（不能累加order by ，否则效率会受影响）
					countSql = hql + where;
					countSql += groupBy == null || groupBy.trim().equals("") ? "" : " group by " + groupBy;
					countSql += otherCause == null || otherCause.trim().equals("") ? "" : " " + otherCause;

					// 生成查询总记录的hql语句。在hql中，不允许在select段内使用子查询，如不允许这种用法：
					// select a,b,(select c from table1) as d from table2 ...
					// 所以可以通过第一个 from 来替换出 count语句。
					countSql = "select count(*) from " + countSql.substring(countSql.toLowerCase().indexOf("from") + 5);
					Query query2 = session.createQuery(countSql);

					if (values != null) {
						for (int i = 0; i <= values.length - 1; i++) {
							if (operators != null && operators[i].equalsIgnoreCase("isnull"))
								continue;
							if (operators != null && operators[i].equalsIgnoreCase("isnotnull"))
								continue;
							if (operators != null && operators[i].equalsIgnoreCase("isempty"))
								continue;
							if (operators != null && operators[i].equalsIgnoreCase("isnotempty"))
								continue;
							query2.setParameter(i, values[i]);
						}
					}

					count = new Integer(String.valueOf(query2.list().get(0)));
				}

				if (offset > 0) {
					query.setFirstResult(offset);
				}

				if (size > 0) {
					query.setMaxResults(size);
				}

				map.put("list", query.list());
				map.put("count", count);
				return map;
			}
		});

		long time2 = System.currentTimeMillis();

		if (isPrintIbatisSql)
			System.out.println("【Hibernate 执行查询时间:" + (time2 - time1) + "ms】");

		return new Object[] { map.get("list"), map.get("count") };
	}

	/**
	 * 同上 query
	 * 
	 * @param hql
	 * @param propertyNames
	 * @param operators
	 * @param values
	 * @param offset
	 * @param size
	 * @param isTotalSize
	 * @return
	 * @throws BaseUncheckedException
	 */
	public Object[] queryPageByCondition(final String hql, final String[] propertyNames, final String[] operators, final Object[] values, final int offset, final int size, final boolean isTotalSize) throws BaseUncheckedException {
		return this.query(hql, propertyNames, operators, values, offset, size, isTotalSize, null, null, null);
	}

	/**
	 * 同上 query
	 * 
	 * @param hql
	 * @param propertyNames
	 * @param operators
	 * @param values
	 * @param offset
	 * @param size
	 * @return
	 * @throws BaseUncheckedException
	 */
	public List queryPageForListByCondition(final String hql, final String[] propertyNames, final String[] operators, final Object[] values, final int offset, final int size) throws BaseUncheckedException {
		Object[] result = this.query(hql, propertyNames, operators, values, offset, size, false, null, null, null);
		return result[0] != null ? (List) result[0] : new ArrayList();
	}

	/**
	 * 同上 query
	 * 
	 * @param hql
	 * @param offset
	 * @param size
	 * @param isTotalSize
	 * @return
	 * @throws BaseUncheckedException
	 */
	public Object[] queryPage(final String hql, final int offset, final int size, final boolean isTotalSize) throws BaseUncheckedException {
		return this.query(hql, null, null, null, offset, size, isTotalSize, null, null, null);
	}

	/**
	 * 同上 query
	 * 
	 * @param hql
	 * @param offset
	 * @param size
	 * @return
	 * @throws BaseUncheckedException
	 */
	public List queryPageForList(final String hql, final int offset, final int size) throws BaseUncheckedException {
		Object[] result = this.query(hql, null, null, null, offset, size, false, null, null, null);
		return result[0] != null ? (List) result[0] : new ArrayList();
	}

	/**
	 * 同上 query
	 * 
	 * @param hql
	 * @param propertyNames
	 * @param operators
	 * @param values
	 * @return
	 * @throws BaseUncheckedException
	 */
	public List queryAllByCondition(final String hql, final String[] propertyNames, final String[] operators, final Object[] values) throws BaseUncheckedException {
		Object[] result = this.query(hql, propertyNames, operators, values, -1, -1, false, null, null, null);
		return result[0] != null ? (List) result[0] : new ArrayList();
	}

	/**
	 * 同上 query
	 * 
	 * @param hql
	 * @return
	 * @throws BaseUncheckedException
	 */
	public List queryAll(final String hql) throws BaseUncheckedException {
		Object[] result = this.query(hql, null, null, null, -1, -1, false, null, null, null);
		return result[0] != null ? (List) result[0] : new ArrayList();
	}

	/**
	 * 【Hibernate分页专用】 依据条件，分页读取list；修改时注意修改 同名方法
	 * 
	 * @param queryConditions
	 *            查询条件,若无条件，设置为NULL
	 * @param pageNo
	 *            查询结果的起始页码。
	 * @param pageSize
	 *            查询结果的最大行数。如果不需要，则设置为-1
	 * @param clazz
	 *            对象的类型
	 * @return
	 * 
	 * @author 孙景弘
	 * 
	 */
	public QueryResult loadByConditions(QueryConditions queryConditions, int pageNo, int pageSize, Class clazz) {
		String hql = "from " + clazz.getSimpleName();
		String[] propertyNames = null;
		String[] operators = null;
		Object[] values = null;
		String orderBy = null;
		String groupBy = null;
		String otherCase = null;

		if (queryConditions != null) {
			propertyNames = StringUtil.listToStrArray(queryConditions.getPropertyNames());
			operators = StringUtil.listToStrArray(queryConditions.getOperators());
			List valuesList = queryConditions.getValues();
			values = valuesList == null || valuesList.size() == 0 ? null : valuesList.toArray();
			orderBy = queryConditions.getOrderBy();
			groupBy = queryConditions.getGroupBy();
			otherCase = queryConditions.getOtherHql();
		}

		pageNo = pageNo <= 1 ? 1 : pageNo;
		int offset = (pageNo - 1) * pageSize;
		offset = offset <= 0 ? 0 : offset;

		// 执行查询
		Object[] result = this.query(hql, propertyNames, operators, values, offset, pageSize, true, orderBy, groupBy, otherCase);

		return new QueryResult((List) result[0], result[1] != null ? ((Integer) result[1]).intValue() : 0);
	}

	/**
	 * 依据条件，不分页读取，一般用在延时加载；修改时注意修改 同名方法
	 * 
	 * @param queryConditions
	 *            查询条件,若无条件，设置为NULL
	 * @param clazz
	 *            对象的类型
	 * @param filter
	 *            dataset过滤条件（一般用于延迟加载），不需要可设置为NULL
	 * @return
	 * 
	 * @author 孙景弘
	 * 
	 */
	public QueryResult loadByConditions(QueryConditions queryConditions, Class clazz, String filter) {
		String hql = "from " + clazz.getSimpleName();

		// 数据集的过滤条件。类似于SQL语句Where后面的查询条件，例如："name='aa' and age>20"。
		// 某些特殊情况下系统会自动生成过滤条件。
		// 例如：使用延迟加载的DataTree时，系统会自动组装成 "parentField=当前结点值"
		// 格式的条件提交到后台，后台可根据此条件执行查询，返回下级结点信息。
		filter = filter == null || filter.equals("") ? "" : " where " + filter;
		hql = hql + filter;

		String[] propertyNames = null;
		String[] operators = null;
		Object[] values = null;
		String orderBy = null;
		String groupBy = null;
		String otherCase = null;

		if (queryConditions != null) {
			propertyNames = StringUtil.listToStrArray(queryConditions.getPropertyNames());
			operators = StringUtil.listToStrArray(queryConditions.getOperators());
			List valuesList = queryConditions.getValues();
			values = valuesList == null || valuesList.size() == 0 ? null : valuesList.toArray();
			orderBy = queryConditions.getOrderBy();
			groupBy = queryConditions.getGroupBy();
			otherCase = queryConditions.getOtherHql();
		}

		// 执行查询
		Object[] result = this.query(hql, propertyNames, operators, values, -1, -1, false, orderBy, groupBy, otherCase);

		return new QueryResult((List) result[0], result[1] != null ? ((Integer) result[1]).intValue() : 0);
	}

	/**
	 * 根据hql批量更新(删除／修改) 注意：此方法直接commit，不在同一事务中，不会回滚。慎用。
	 * 
	 * 【1】批量删除 String hql = delete Student s where s.sage>25; int intCount = this.batchUpdate(hql);
	 * 
	 * 【2】批量更新 String hql = update Student s set s.sage='22' where s.id=11; int intCount = this.batchUpdate(hql);
	 * 
	 * @param hql
	 *            HQL更新及删除语句
	 * 
	 * @return 更新或删除成功的记录数
	 */
	public int batchUpdate(String hql) {
		long time1 = System.currentTimeMillis();

		int result = 0;
		Session session = getHibernateTemplate().getSessionFactory().openSession();

		Transaction trans = null;
		try {
			trans = session.beginTransaction();
			result = session.createQuery(hql).executeUpdate();
			trans.commit();
			trans = null;
		} catch (HibernateException e) {
			if (trans != null)
				trans.rollback();
			e.printStackTrace();
			throw e;
		} finally {
			session.close();
		}

		long time2 = System.currentTimeMillis();

		if (isPrintIbatisSql)
			System.out.println("【Hibernate 执行update时间:" + (time2 - time1) + "ms】");

		return result;
	}

	/**
	 * 根据hql批量更新(删除／修改)。注意：此方法直接commit，不在同一事务中，不会回滚。慎用。
	 * 
	 * 【1】批量删除 String hql = delete Student s where s.sage>25; int intCount = this.batchUpdate(hql);
	 * 
	 * 【2】批量更新 String hql = update Student s set s.sage='22' where s.id=11; int intCount = this.batchUpdate(hql);
	 * 
	 * @param hql
	 *            HQL更新及删除语句
	 * 
	 * @return 更新或删除成功的记录数
	 */
	public int batchUpdate(String hql, Object[] values) {
		long time1 = System.currentTimeMillis();

		int result = 0;
		Session session = getHibernateTemplate().getSessionFactory().openSession();

		Transaction trans = null;
		try {
			trans = session.beginTransaction();
			Query query = session.createQuery(hql);
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
			result = query.executeUpdate();
			trans.commit();
			trans = null;
		} catch (HibernateException e) {
			if (trans != null)
				trans.rollback();
			e.printStackTrace();
			throw e;
		} finally {
			session.close();
		}

		long time2 = System.currentTimeMillis();

		if (isPrintIbatisSql)
			System.out.println("【Hibernate 执行update时间:" + (time2 - time1) + "ms】");

		return result;
	}

	/**
	 * 根据传入参数查询hql语句。
	 * 
	 * @param hql
	 *            HQL查询语句 Object values HQL查询语句中‘？’对应的参数，逐一对应。 一般多用于传入参数是日期类型的HQL。<br>
	 *            如： String hqlselectUser = "from TGzSalary a where a.userId='" + userId + "'" + "and a.salaryDate =?"; <br>
	 *            super.queryByParams(hqlselectUser, new Object[] { salaryDate });注：salaryDate为Date类型<br>
	 * @return 更新或删除成功的记录数
	 */
	public List queryByParams(String hql, Object[] values) {
		long time1 = System.currentTimeMillis();

		List result = null;
		Session session = getHibernateTemplate().getSessionFactory().openSession();

		Transaction trans = null;
		try {
			trans = session.beginTransaction();
			Query query = session.createQuery(hql);
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
			result = query.list();
			trans.commit();
			trans = null;
		} catch (HibernateException e) {
			if (trans != null)
				trans.rollback();
			e.printStackTrace();
			throw e;
		} finally {
			session.close();
		}

		long time2 = System.currentTimeMillis();

		if (isPrintIbatisSql)
			System.out.println("【Hibernate 执行update时间:" + (time2 - time1) + "ms】");

		return result;
	}

	/**
	 * iBatis 插入操作
	 * 
	 * 胡砥峰提示：不到万不得已【解决性能问题】，不建议用iBatis操作数据！！请用Hibernate!!
	 * 
	 * @param statementId
	 * @param value
	 * @return
	 */
	public Object insertBySql(String statementId, Object value) {
		return this.getSqlMapClientTemplate().insert(statementId, value);
	}

	/**
	 * iBatis 删除操作
	 * 
	 * 胡砥峰提示：不到万不得已【解决性能问题】，不建议用iBatis操作数据！！请用Hibernate!!
	 * 
	 * @param statementId
	 * @param value
	 * @return
	 */
	public int deleteBySql(String statementId, Object value) {
		return this.getSqlMapClientTemplate().delete(statementId, value);
	}

	/**
	 * iBatis 修改操作
	 * 
	 * 胡砥峰提示：不到万不得已【解决性能问题】，不建议用iBatis操作数据！！请用Hibernate!!
	 * 
	 * @param statementId
	 * @param value
	 * @return
	 */
	public int updateBySql(String statementId, Object value) {
		return this.getSqlMapClientTemplate().update(statementId, value);
	}

	/**
	 * 获取iBatis的真正执行语句，测试驱动开发时候要用
	 * 
	 * @param statementId
	 * @param object
	 * @return
	 */
	private String getIbatisSql(String statementId, Object object) {
		SqlMapClientImpl sqlMapClientImpl = (SqlMapClientImpl) sqlMapClient;
		MappedStatement mappedStatement = sqlMapClientImpl.getMappedStatement(statementId);
		SessionScope sessionScope = new SessionScope();
		StatementScope statementScope = new StatementScope(sessionScope);
		statementScope.setStatement(mappedStatement);

		return mappedStatement.getSql().getSql(statementScope, object);
	}

	/**
	 * iBatis 查询记录list(不分页查询调用)
	 * 
	 * 
	 * @param statementId
	 *            调用iBatis的SqlMap文件的声明段名，规则名：SqlMap的namespace+"." + 该sqlMap文件某片段的id
	 * @param object
	 *            查询条件值
	 * 
	 * @return list 找到的记录
	 */
	public List queryForListByIbatis(String statementId, Object object) {
		long time1 = System.currentTimeMillis();

		List list = this.getSqlMapClientTemplate().queryForList(statementId, object);

		long time2 = System.currentTimeMillis();

		if (isPrintIbatisSql) {
			System.out.println("【iBatis 执行SQL为:" + this.getIbatisSql(statementId, object) + "】");
			System.out.println("【iBatis 执行查询时间:" + (time2 - time1) + "ms】");
		}

		return list;
	}

	/**
	 * iBatis 查询记录list(固定记录数查询调用)
	 * 
	 * 
	 * @param statementId
	 *            调用iBatis的SqlMap文件的声明段名，规则名：SqlMap的namespace+"." + 该sqlMap文件某片段的id
	 * @param object
	 *            查询条件值
	 * @param offset
	 *            返回查询结果的起始行，从0开始
	 * @param size
	 *            返回查询结果的最大行数
	 * 
	 * @return list 找到的记录
	 */
	public List queryForListByIbatis(String statementId, Object object, int offset, int size) {
		long time1 = System.currentTimeMillis();

		List list = getSqlMapClientTemplate().queryForList(statementId, object, offset, size);

		long time2 = System.currentTimeMillis();

		if (isPrintIbatisSql) {
			System.out.println("【iBatis 执行SQL为:" + this.getIbatisSql(statementId, object) + "】");
			System.out.println("【iBatis 执行查询时间:" + (time2 - time1) + "ms】");
		}

		return list;
	}

	/**
	 * 【Ibatis分页专用】
	 * 
	 * @param queryConditions
	 * @param pageNo
	 * @param pageSize
	 * @return
	 */
	public QueryResult queryForPageByIbatis(QueryConditions queryConditions, int pageNo, int pageSize) {
		long time1 = System.currentTimeMillis();

		pageNo = pageNo <= 1 ? 1 : pageNo;
		int offset = (pageNo - 1) * pageSize;
		offset = offset <= 0 ? 0 : offset;

		List list = null;
		// 原始执行sql
		String sql = this.getIbatisSql(queryConditions.getStatementId(), queryConditions.getParamValue());

		this.setEnableCustomizedPaged(false); // 不用orm分页/DB2下测试发现效率有问题，所以采用IBATIS自封装的分页。

		// 处理分页count数量
		int count = 0;
		if (StringUtils.isNotEmpty(queryConditions.getCountStatementId())) {// 是否用自定义的count语句
			list = this.getSqlMapClientTemplate().queryForList(queryConditions.getStatementId(), queryConditions.getParamValue(), offset, pageSize);
			// count语句来自自行写的ibaits
			count = (Integer) this.queryForObjectByIbatis(queryConditions.getCountStatementId(), queryConditions.getParamValue());
		} else {
			if (this.sqlExecutor instanceof IBatisSqlExecutor)
				((IBatisSqlExecutor) this.sqlExecutor).setNeedCount(true);// 设置需要用到自定义的计算count方法

			if (isPrintIbatisSql) {
				// 加工成 count sql
				String countSql = IBatisUtils.getCountSQL(sql);
				System.out.println("【iBatis 执行CountSQL为:" + countSql + "】");
			}

			// 传入countBuffer，返回记录数：
			list = this.getSqlMapClientTemplate().queryForList(queryConditions.getStatementId(), queryConditions.getParamValue(), offset, pageSize);
			count = ((IBatisSqlExecutor) this.sqlExecutor).getCount();
		}

		long time2 = System.currentTimeMillis();

		if (isPrintIbatisSql) {
			System.out.println("【iBatis 执行SQL为:" + this.getIbatisSql(queryConditions.getStatementId(), queryConditions.getParamValue()) + "】");
			System.out.println("【iBatis 执行查询时间:" + (time2 - time1) + "ms】");
		}

		return new QueryResult(list, count);
	}

	/**
	 * 【Ibatis分页专用】iBatis 查询记录Dataset(整合分页/不分页)
	 * 
	 * @param statementId
	 *            调用iBatis的SqlMap文件的声明段名，规则名：SqlMap的namespace+"." + 该sqlMap文件某片段的id
	 * @param countStatementId
	 *            传入的自定义的count语句， 调用iBatis的SqlMap文件的声明段名。若不用自定义的count语句，可以设置为NULL
	 * @param paramValue
	 *            查询条件值
	 * @param isAppend
	 *            是否把list追加到dataset后面
	 * 
	 * @return 返回dataset数据集
	 */
	public Dataset queryForPageByIbatis(String statementId, String countStatementId, Object paramValue, Dataset dataset, boolean isAppend) {

		if (dataset.getPageSize() <= 0) { // 判断DATASET是否需要分页
			List list = queryForListByIbatis(statementId, paramValue);
			return BeanUtil.listToDataset(list, dataset, isAppend);
		}

		QueryConditions queryConditions = new QueryConditions();
		queryConditions.setParameter(statementId, countStatementId, paramValue);

		QueryResult queryResult = this.queryForPageByIbatis(queryConditions, dataset.getPageNo(), dataset.getPageSize());

		return BeanUtil.queryResultToDataset(queryResult, dataset, isAppend, true);
	}

	/**
	 * 【Ibatis分页专用】iBatis 查询记录Dataset( 分页/ 但总记录数为自增长。适合记录数太大，查询COUNT语句效率太低的情况。备注：需要配合DATASET动态构造来使用。)
	 * 
	 * @param statementId
	 *            调用iBatis的SqlMap文件的声明段名，规则名：SqlMap的namespace+"." + 该sqlMap文件某片段的id
	 * @param paramValue
	 *            查询条件值
	 * @param isAppend
	 *            是否把list追加到dataset后面
	 * 
	 * @return 返回dataset数据集
	 */
	public Dataset queryForPageByIbatisAutoCount(String statementId, Object paramValue, Dataset dataset, boolean isAppend) {
		long time1 = System.currentTimeMillis();

		int pageNo = dataset.getPageNo();
		int pageSize = dataset.getPageSize();
		pageNo = pageNo <= 1 ? 1 : pageNo;
		int offset = (pageNo - 1) * pageSize;
		offset = offset <= 0 ? 0 : offset;

		this.setEnableCustomizedPaged(false); // 不用orm分页
		List list = this.getSqlMapClientTemplate().queryForList(statementId, paramValue, offset, pageSize);

		int listSize = list.size();

		int total = pageSize * (pageNo - 1);
		if (listSize == pageSize) {
			total = total + listSize + pageSize;
		} else {
			total = total + listSize;
		}
		BeanUtil.listToDataset(list, dataset, isAppend);
		dataset.setTotalSize(total);

		long time2 = System.currentTimeMillis();
		if (isPrintIbatisSql) {
			System.out.println("【iBatis 执行SQL为:" + this.getIbatisSql(statementId, paramValue) + "】");
			System.out.println("【iBatis 执行查询时间:" + (time2 - time1) + "ms】");
		}

		return dataset;
	}

	/**
	 * iBatis 查询记录，返回Object对象。
	 * 
	 * @param statementId
	 *            调用iBatis的SqlMap文件的声明段名，规则名：SqlMap的namespace+"." + 该sqlMap文件某片段的id
	 * @param object
	 *            查询条件值
	 * 
	 * @return Object 找到的记录
	 */
	public Object queryForObjectByIbatis(String statementId, Object object) {
		long time1 = System.currentTimeMillis();

		Object bean = this.getSqlMapClientTemplate().queryForObject(statementId, object);

		long time2 = System.currentTimeMillis();
		if (isPrintIbatisSql) {
			System.out.println("【iBatis 执行SQL为:" + this.getIbatisSql(statementId, object) + "】");
			System.out.println("【iBatis 执行查询时间:" + (time2 - time1) + "ms】");
		}

		return bean;
	}

	/**
	 * 设置是否需要分页，影响orm的执行；解决ibatis跨数据库分页
	 * 
	 * @param enableCustomizedPaged
	 */
	public void setEnableCustomizedPaged(boolean enableCustomizedPaged) {
		if (this.sqlExecutor instanceof IBatisSqlExecutor)
			((IBatisSqlExecutor) this.sqlExecutor).setEnableCustomizedPaged(enableCustomizedPaged);
	}

	/**
	 * 为Entity 添加权限控制属性值
	 * 
	 * @param entity
	 * @param sessionObject
	 * @param isSave
	 *            是否是新增的操作。true:新增操作；false:更新操作
	 * @return
	 * 
	 * @author 孙景弘
	 */
	public <T> T setAuthPropetryToEntity(T entity, SessionObject sessionObject, boolean isSave) {
		Date nowDate = new Date();
		if (isSave) {
			ReflectUtil.setFieldValue(entity, "createOrgId", String.class, sessionObject.getOrgId());
			ReflectUtil.setFieldValue(entity, "createOrgName", String.class, sessionObject.getOrgName());
			ReflectUtil.setFieldValue(entity, "orgLevelCode", String.class, sessionObject.getOrgLevelCode());
			ReflectUtil.setFieldValue(entity, "createUserId", String.class, sessionObject.getUserId());
			ReflectUtil.setFieldValue(entity, "createUserName", String.class, sessionObject.getUserName());
			ReflectUtil.setFieldValue(entity, "createTime", Date.class, nowDate);
		}

		ReflectUtil.setFieldValue(entity, "updateTime", Date.class, nowDate);
		ReflectUtil.setFieldValue(entity, "updateUserId", String.class, sessionObject.getUserId());
		ReflectUtil.setFieldValue(entity, "updateUserName", String.class, sessionObject.getUserName());

		return entity;
	}

	/**
	 * 胡砥峰提示：工作流 方法; 启动流程
	 * 
	 * @param context
	 * @param templateId
	 * @param sessionObject
	 * @throws BaseCheckedException
	 */
	public void saveFlowLaunch(IActionContext context, String templateId, SessionObject sessionObject) throws BaseCheckedException {

		// 启动新流程
		List flowNodeList = this.queryAll("from TSysFlowNode where templateId = '" + templateId + "' and nodeType = '1'");
		if (flowNodeList != null && flowNodeList.size() == 1) {
			TSysFlowNode currentFlowNode = (TSysFlowNode) flowNodeList.get(0); // 获得启动节点

			// 组织一个 流程实例， 并插入数据库
			String instanceId = UniqueKeyUtil.getUuidKey(); // 实例获取 唯一的 UUID
			TSysFlowInstance tSysFlowInstance = new TSysFlowInstance();
			tSysFlowInstance.setInstanceId(instanceId);
			tSysFlowInstance.setTemplateId(templateId);
			tSysFlowInstance.setLaunchUserName(sessionObject.getUserName());
			tSysFlowInstance.setLaunchTime(DateUtil.getDateTime());
			tSysFlowInstance.setLaunchUserId(sessionObject.getUserId());
			tSysFlowInstance.setLaunchOrgId(sessionObject.getOrgId());
			tSysFlowInstance.setLaunchOrgName(sessionObject.getOrgName());
			tSysFlowInstance.setStatus("0"); // 未完结流程0; 已完结流程1
			this.save(tSysFlowInstance);

			// 组织一个 启动流程步骤， 并插入数据库
			TSysFlowStep tSysFlowStep = new TSysFlowStep();
			tSysFlowStep.setInstanceId(instanceId);
			tSysFlowStep.setNodeId(currentFlowNode.getNodeId());
			tSysFlowStep.setNodeName(currentFlowNode.getNodeName());
			tSysFlowStep.setReceivedTime(DateUtil.getDateTime());
			tSysFlowStep.setDoUserId(sessionObject.getUserId());
			tSysFlowStep.setDoUserName(sessionObject.getUserName());
			tSysFlowStep.setDoComment("提出申请");
			tSysFlowStep.setDoTime(DateUtil.getDateTime());
			tSysFlowStep.setStatus("1");
			this.save(tSysFlowStep);

			this.saveFlowBizData(context, instanceId); // 保存业务数据 由子类覆写

			// 转向下一个节点
			this.turnToNextNode(context, currentFlowNode, instanceId);
		} else {
			throw new BaseCheckedException("", "工作流异常：在流程模板中找不到启动节点或找到多个启动节点!");
		}
	}

	/**
	 * 胡砥峰提示：工作流 方法; 提交流程，进入下一步 节点
	 * 
	 * @param context
	 * @param templateId
	 * @param sessionObject
	 * @throws BaseCheckedException
	 */
	public void saveFlowNextStep(IActionContext context, String templateId, SessionObject sessionObject) throws BaseCheckedException {
		// 已启动的流程，根据不同的条件参数，转到下一个节点
		String instanceId = StringUtils.trimToEmpty(context.getCommandParameterValue("instanceId"));

		if (StringUtils.isEmpty(instanceId)) {
			throw new BaseCheckedException("", "工作流异常：流程实例id为空!");
		}

		this.saveFlowBizData(context, instanceId); // 保存业务数据 由子类覆写

		// 【1. 读取当前step，】
		List flowStepList = this.queryAll("from TSysFlowStep where instanceId='" + instanceId + "' and status='0' and doUserId='" + sessionObject.getUserId() + "'"); // 应该是一条记录
		if (flowStepList.size() != 1) {
			throw new BaseCheckedException("", "工作流异常：当前操作人针对此流程没有审批步骤或有多个审批步骤!");
		} else {
			TSysFlowStep currentFlowStep = (TSysFlowStep) flowStepList.get(0); // 当前步骤
			// 【注意：】 jsp审批页面 审批意见 的text Id 一定要为 Text_doComment！
			String comment = context.getElementValue("Text_doComment");
			currentFlowStep.setDoComment(comment);
			currentFlowStep.setDoTime(DateUtil.getDateTime());
			currentFlowStep.setStatus("1");
			this.update(currentFlowStep); // 最后：当前步骤更新成 已处理。

			// 【2. 检查当前节点 是 抢占式 还是 并行式】
			TSysFlowNode currentFlowNode = this.get(TSysFlowNode.class, currentFlowStep.getNodeId());

			// 判断是否是结束节点 且 不是驳回操作。
			if ("9".equals(currentFlowNode.getNodeType()) && !"rejectToLaunch".equals(context.getElementValue("Text_reject"))) {
				// 将流程实例的状态设为1,已处理完毕
				TSysFlowInstance flowInstance = this.get(TSysFlowInstance.class, instanceId);
				flowInstance.setStatus("1");
				this.update(flowInstance);

				// 胡砥峰2010-10-17发现：如果是多人审批最后一个节点，要删除其他的审批人的step
				this.deleteAll(this.queryAll("from TSysFlowStep where instanceId='" + instanceId + "' and status='0'"));

				this.saveFlowBizDataAfterFlowEnd(context, instanceId); // 流程结束后,执行该方法 由子类覆写

				return;
			}

			// 2010-09-27胡砥峰修改bug。 如果驳回到启动修改节点，需要删除该节点 其他 处理人的 step（当这个节点有好几个人处理时）。
			// 【处理驳回的特殊情况：驳回到启动 rejectToLaunch; 读取前台jsp的 Text_reject 的值】
			String reject = StringUtils.trimToEmpty(context.getElementValue("Text_reject"));
			if ("rejectToLaunch".equals(reject)) {
				this.deleteAll(this.queryAll("from TSysFlowStep where instanceId='" + instanceId + "' and status='0'"));
			}

			// 以下代码处理 抢占式节点 或 并行式节点
			if ("1".equals(currentFlowNode.getHandleMode())) {
				// 抢占式节点, 删除其他status为0的 FlowStep。 生成下一个节点的 FlowStep
				this.deleteAll(this.queryAll("from TSysFlowStep where instanceId='" + instanceId + "' and status='0'"));

				// 转向下一个节点
				this.turnToNextNode(context, currentFlowNode, instanceId);
			} else {
				// 并行式节点, 判断 还有没有 status为0的 FlowStep， 如果有则不处理， 如果没有则 生成下一个节点的 FlowStep
				List noDoList = this.queryAll("from TSysFlowStep where instanceId='" + instanceId + "' and status='0'");
				if (noDoList.size() == 0) {
					// 转向下一个节点
					this.turnToNextNode(context, currentFlowNode, instanceId);
				}
			}
		}
	}

	/**
	 * 胡砥峰提示：工作流 私有方法; 转向下一个节点
	 * 
	 * @param context
	 * @param currentFlowNode
	 * @param instanceId
	 */
	private void turnToNextNode(IActionContext context, TSysFlowNode currentFlowNode, String instanceId) throws BaseCheckedException {
		TSysFlowInstance flowInstance = this.get(TSysFlowInstance.class, instanceId); // 流程实例对象
		TSysFlowNode nextFlowNode = null; // 下一个处理节点

		// 【处理驳回的特殊情况：驳回到启动 rejectToLaunch; 读取前台jsp的 Text_reject 的值】
		String reject = StringUtils.trimToEmpty(context.getElementValue("Text_reject"));
		if ("rejectToLaunch".equals(reject)) {
			List nodeList = this.queryAll("from TSysFlowNode where templateId='" + flowInstance.getTemplateId() + "' and nodeType='8'");
			if (nodeList.size() != 1)
				throw new BaseCheckedException("", "工作流异常：驳回到启动节点时找不到【被驳回到启动节点】或找到多个【被驳回到启动节点】!");

			nextFlowNode = (TSysFlowNode) nodeList.get(0);

			// 组织一个 流程步骤， 并插入数据库
			TSysFlowStep nextFlowStep = new TSysFlowStep();
			nextFlowStep.setInstanceId(instanceId);
			nextFlowStep.setNodeId(nextFlowNode.getNodeId());
			nextFlowStep.setNodeName(nextFlowNode.getNodeName());
			nextFlowStep.setReceivedTime(DateUtil.getDateTime());
			nextFlowStep.setDoUserId(flowInstance.getLaunchUserId());
			nextFlowStep.setDoUserName(flowInstance.getLaunchUserName());
			nextFlowStep.setStatus("0");
			this.save(nextFlowStep);

			flowInstance.setStatus("0"); // 被驳回状态
			this.update(flowInstance);

			return;
		}

		// 获取当前节点的 路由list
		List<TSysFlowRoute> flowRouteList = this.queryAll("from TSysFlowRoute where nodeId = '" + currentFlowNode.getNodeId() + "'");

		if (flowRouteList.size() == 0)
			throw new BaseCheckedException("", "工作流异常：当前节点没有路由信息!");

		// 如果只有一条路由信息，则直接跳转
		if (flowRouteList.size() == 1) {
			TSysFlowRoute flowRoute = flowRouteList.get(0);
			nextFlowNode = this.get(TSysFlowNode.class, flowRoute.getNextNodeId());
		} else {
			// 如果有多条记录，循环遍历路由条件，以第一个符合条件的路由条件为跳转路由
			Iterator<TSysFlowRoute> iterator = flowRouteList.iterator();
			while (iterator.hasNext()) {
				TSysFlowRoute flowRoute = iterator.next();
				String routeCondition = flowRoute.getRouteCondition(); // 条件表达式
				// 如果条件为空，则默认走此路由
				if (StringUtils.isEmpty(routeCondition)) {
					nextFlowNode = this.get(TSysFlowRoute.class, flowRoute.getNextNodeId());
					break;// 找到正确路由，跳出循环
				}

				// 根据 路由id, 获取该路由的参数列表,根据参数的name，获取参数值，放入 condition 里面去判断。
				List<TSysFlowParam> flowParamList = this.queryAll("from TSysFlowParam where routeId = '" + flowRoute.getRouteId() + "'");

				List<Variable> variables = new ArrayList<Variable>(); // 表达式变量列表
				for (int i = 0; i < flowParamList.size(); i++) {
					TSysFlowParam flowParam = flowParamList.get(i);

					// 从页面上取业务的参数（【注意】页面Text控件名称必须和数据库PARAM里的PARAM_NAME字段值一样）
					String paramValue = context.getElementValue("Text_" + flowParam.getParamName());
					if ("1".equals(flowParam.getDataType())) {
						variables.add(Variable.createVariable(flowParam.getParamName(), paramValue)); // 参数类型为 字符串
					} else if ("2".equals(flowParam.getDataType())) {
						variables.add(Variable.createVariable(flowParam.getParamName(), Integer.valueOf(paramValue))); // 参数类型为 整数
					} else if ("3".equals(flowParam.getDataType())) {
						variables.add(Variable.createVariable(flowParam.getParamName(), Boolean.valueOf(paramValue))); // 参数类型为 布尔型
					}
				}

				Object result = ExpressionEvaluator.evaluate(flowRoute.getRouteCondition(), variables); // 表达式计算结果

				// 如果表达式结果不为null并且为true，走此条路由
				if (result != null && Boolean.parseBoolean(result.toString())) {
					nextFlowNode = this.get(TSysFlowNode.class, flowRoute.getNextNodeId());

					// 开始【处理 上一节点 指定下一步处理人 的特殊情况： 读取前台jsp的 Text_firstDoMan 的值】
					String firstDoMan = StringUtils.trimToEmpty(context.getElementValue("Text_firstDoMan"));
					if (!StringUtil.isEmpty(firstDoMan)) {
						String[] firstDoManArray = StringUtil.split(firstDoMan, ",");
						for (int i = 0; i < firstDoManArray.length; i++) {
							String doUserId = StringUtils.trim(firstDoManArray[i]);
							String doUserName = ((TSysUser) this.get(TSysUser.class, doUserId)).getUserName();

							// 组织一个 流程步骤， 并插入数据库
							TSysFlowStep nextFlowStep = new TSysFlowStep();
							nextFlowStep.setInstanceId(instanceId);
							nextFlowStep.setNodeId(nextFlowNode.getNodeId());
							nextFlowStep.setNodeName(nextFlowNode.getNodeName());
							nextFlowStep.setReceivedTime(DateUtil.getDateTime());
							nextFlowStep.setDoUserId(doUserId);
							nextFlowStep.setDoUserName(doUserName);
							nextFlowStep.setStatus("0");
							this.save(nextFlowStep);
						}

						// 将 流程实例状态 改回0， 为了配合 已驳回的状态
						if ("9".equals(flowInstance.getStatus())) {
							flowInstance.setStatus("0");
							this.update(flowInstance);
						}

						return;
					}
					// 结束【处理 上一节点 指定下一步处理人 的特殊情况： 读取前台jsp的 Text_firstDoMan 的值】

					break;// 找到正确路由，跳出循环
				}
			}
		}

		if (nextFlowNode == null)
			throw new BaseCheckedException("", "工作流异常：找不到符合条件的下一个跳转节点!");

		// 根据当前节点的 流程用户组的人员关系， 组织一个或多个 流程步骤， 并插入数据库
		if (StringUtils.isEmpty(nextFlowNode.getTeamId())) {
			throw new BaseCheckedException("", "工作流异常：下一个流程处理节点，找不到处理人员!");
		} else {
			// 读取流程用户组，根据 流程用户组人员与启动人 关系，进行过滤
			TSysFlowTeam flowTeam = (TSysFlowTeam) this.get(TSysFlowTeam.class, nextFlowNode.getTeamId());
			List doUserIdList = StringUtil.strToList(flowTeam.getUserIdList(), ",");

			boolean flag = false; // 是否有下一步操作人出现
			for (int i = 0; i < doUserIdList.size(); i++) {
				TSysUser sysUser = (TSysUser) this.get(TSysUser.class, (String) doUserIdList.get(i));

				if ("1".equals(nextFlowNode.getTeamLauncher())) {
					// 下一节点处理人必须和 流程启动人 同机构，处理人可能有很多人，如果选择了与启动人同机构，则过滤掉不同机构的处理人。
					if (!sysUser.getOrgId().equals(flowInstance.getLaunchOrgId()))
						continue;
				}

				// 组织一个 流程步骤， 并插入数据库
				TSysFlowStep nextFlowStep = new TSysFlowStep();
				nextFlowStep.setInstanceId(instanceId);
				nextFlowStep.setNodeId(nextFlowNode.getNodeId());
				nextFlowStep.setNodeName(nextFlowNode.getNodeName());
				nextFlowStep.setReceivedTime(DateUtil.getDateTime());
				nextFlowStep.setDoUserId(sysUser.getUserId());
				nextFlowStep.setDoUserName(sysUser.getUserName());
				nextFlowStep.setStatus("0");
				this.save(nextFlowStep);
				flag = true;

				// 将 流程实例状态 改回0， 为了配合 已驳回的状态
				if ("9".equals(flowInstance.getStatus())) {
					flowInstance.setStatus("0");
					this.update(flowInstance);
				}
			}
			if (!flag) {
				throw new BaseCheckedException("", "工作流异常：根据条件，下一步节点没有处理人!");
			}
		}
	}

	/**
	 * 胡砥峰提示：工作流 方法; 保存流程 业务数据, 由子类覆写
	 * 
	 * @param context
	 * @param instanceId
	 * @throws BaseCheckedException
	 */
	public void saveFlowBizData(IActionContext context, String instanceId) throws BaseCheckedException {
		// 由子类覆写
	}

	/**
	 * 胡砥峰提示：工作流 方法; 流程结束后,执行该方法, 由子类覆写
	 * 
	 * @param context
	 * @param instanceId
	 * @throws BaseCheckedException
	 */
	public void saveFlowBizDataAfterFlowEnd(IActionContext context, String instanceId) throws BaseCheckedException {
		// 由子类覆写
	}

}
